package schoolsource.security;

import java.util.HashMap;
import java.util.logging.Level;

import schoolsource.gui.FormFields;
import schoolsource.model.IntegerItem;
import schoolsource.model.PasswordItem;
import schoolsource.model.StringItem;
import schoolsource.sql.SchoolDBSQL;
import schoolsource.util.SchoolLogger;

/**
 * 
 * @author Steven Steele
 * Copyright 2003
 *
 * Contains all security methods required to validate users identities/tokens. 
 * 
 */
public class SecurityAuthorizer {
	
	// A container for all security tokens
	private static HashMap securityTokenMap = new HashMap();

	/**
	 * Ensures a token is valid for the given user type.
	 * @param token A string token issued to the user.
	 * @param type The type of user ('S', 'T', 'P', 'A')
	 * @return The security token associated with the token, if the token is valid for the user type.
	 */
	public static SecurityToken validateToken(String token, char type) {

		SecurityToken st = (SecurityToken) securityTokenMap.get(token);

		if (st != null) {
			if (((Character) st.getValue(FormFields.USRTYPE)).charValue()
				!= type) {
				SchoolLogger.logger.log(Level.WARNING, "Unmatched user type - hack attempt from: ");
				st.printValues();
			} else {
				st.interrupt();
			}
		}

		SchoolLogger.logger.log(Level.INFO, "securityToken is null: " + (st == null));

		return st;

	}

	/**
	 * Indicates whether a username and password is valid for a student. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param username The username of the student.
	 * @param password The password of the student.
	 * @return True if the username, password, and user type match and there have been fewer
	 * than 3 failed login attempts prior to this attempt.
	 */
	public static boolean isValidStudentLogin(String username, String password, String dbName) {
		IntegerItem failed;
		boolean returnBoolean = true;
		StringItem un = new StringItem(SchoolDBSQL.USRSCHOOLID, username, true);
		PasswordItem pw = null;
		StringItem type;
		type = new StringItem(SchoolDBSQL.USRTYPE, SchoolDBSQL.STUDENT, true);
		pw = new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);
		//	   TODO: Add IP address recording and disabling functionality

		failed = SchoolDBSQL.getSchoolDBSQL(dbName).verifyUser(un, pw, type);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}

	/**
	 * Indicates whether a username and password is valid for a parent. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param parentUsername The username of the parent.
	 * @param studentUsername The username of the student related to the parent.
	 * @param password The password of the parent.
	 * @return True if the parent's username, student's username, parent's password, and user
	 * type match and there have been fewer than 3 failed login attempts prior to this attempt. 
	 */
	public static boolean isValidParentLogin(String parentUsername, String studentUsername, String password, String dbName) {
		boolean returnBoolean = true;
		StringItem pun =
			new StringItem(SchoolDBSQL.USRSCHOOLID, parentUsername, true);
		StringItem sun =
			new StringItem(SchoolDBSQL.USRSCHOOLID, studentUsername, true);
		PasswordItem pw =
			new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);
		StringItem type =
			new StringItem(SchoolDBSQL.USRTYPE, SchoolDBSQL.STUDENT, true);

		//	   TODO: Add IP address recording and disabling functionality

		IntegerItem failed = SchoolDBSQL.getSchoolDBSQL(dbName).verifyParent(pun, sun, pw);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}

	/**
	 * Indicates whether a username and password is valid for a teacher. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param username The username of the teacher.
	 * @param password The password of the teacher.
	 * @return True if the teacher's username, password, and user type match, and there have been
	 * fewer than 3 failed login attempts prior to this attempt.
	 */
	public static boolean isValidTeacherLogin(String username, String password, String dbName) {
		IntegerItem failed;
		boolean returnBoolean = true;
		StringItem un = new StringItem(SchoolDBSQL.USRSCHOOLID, username, true);
		StringItem type =
			new StringItem(SchoolDBSQL.USRTYPE, SchoolDBSQL.TEACHER, true);
		PasswordItem pw = null;
		pw = new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);

		failed = SchoolDBSQL.getSchoolDBSQL(dbName).verifyUser(un, pw, type);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}

	/**
	 * Indicates whether a username and password is valid for an adminstrator. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param username The username of the administrator.
	 * @param password The password of the administrator.
	 * @return True if the administrator's username, password, and user type match, and there
	 * have been fewer than 3 failed login attempts prior to this attempt.
	 */
	public static boolean isValidAdminLogin(String username, String password, String dbName) {
		IntegerItem failed;
		boolean returnBoolean = true;
		StringItem un = new StringItem(SchoolDBSQL.USRSCHOOLID, username, true);
		StringItem type =
			new StringItem(SchoolDBSQL.USRTYPE, SchoolDBSQL.ADMIN, true);
		PasswordItem pw = null;
		pw = new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);

		failed = SchoolDBSQL.getSchoolDBSQL(dbName).verifyUser(un, pw, type);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}

	/**
	 * Indicates whether a username and password is valid for a user type. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param username The username of the user.
	 * @param password The password of the user.
	 * @param typeString The type of user ("T", "S", "P", "A")
	 * @return True if the user's username, password, and user type match, and there have
	 * been fewer than 3 failed login attempts prior to this attempt.
	 */
	public static boolean isValidLogin(String username, String password, String typeString, String dbName) {
		IntegerItem failed;
		boolean returnBoolean = true;
		StringItem un = new StringItem(SchoolDBSQL.USRSCHOOLID, username, true);
		PasswordItem pw = null;
		StringItem type = null;
		pw = new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);
		type = new StringItem(SchoolDBSQL.USRTYPE, typeString, true);

		failed = (IntegerItem) SchoolDBSQL.getSchoolDBSQL(dbName).verifyUser(un, pw, type);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}

	/**
	 * Indicates whether a username and password is valid for a student. Validity requires
	 * no more than 2 login failures prior to a successful login.
	 * @param assignedID The auto-generated user ID of the user.
	 * @param password The password of the user.
	 * @return True if the user's username and password match, and there have
	 * been fewer than 3 failed login attempts prior to this attempt.
	 */
	public static boolean isValidLogin(String assignedID, String password, String dbName) {
		IntegerItem failed;
		boolean returnBoolean = true;
		StringItem un =
			new StringItem(SchoolDBSQL.USRASSIGNEDID, assignedID, true);
		PasswordItem pw = null;
		pw = new PasswordItem(SchoolDBSQL.USRPASSWORD, password, true);

		failed = SchoolDBSQL.getSchoolDBSQL(dbName).verifyUser(un, pw);
		// look up token in table, if it exists then return true.
		// reset the associated time
		int temp = ((Integer) failed.getObject()).intValue();
		if (temp < 0 || temp > 2) {
			returnBoolean = false;
		}

		return returnBoolean;
	}
	
	/**
	 * Returns a token that can be used to store user information. This token is
	 * stored independently, but will time out after 10 minutes of inactivity.
	 * @return A security token where user information can be stored and the identity
	 * of the user can be verified.
	 */
	public static SecurityToken generateToken() {
		String token = generateTokenString();
		SecurityToken st = new SecurityToken();
		st.setTokenKey(token);
		securityTokenMap.put(token, st);
		st.start();
		SchoolLogger.logger.log(Level.INFO, "Token: " + token);
		return st;
	}

	/**
	 * Removes the security token indicated by the string token from independent storage.
	 * @param token The string token identifying the security token to remove from independent storage.
	 */
	public static void removeToken(String token) {
		SchoolLogger.logger.log(Level.INFO, "Removing token " + token);
		securityTokenMap.remove(token);
	}

	/**
	 * Generates a string token to associate with a security token.
	 * @return A string.
	 */
	private static String generateTokenString() {
		return String.valueOf(System.currentTimeMillis());
	}
}
